package com.lightsh.object;

import org.jbox2d.common.Vec2;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.MotionEvent;

import com.lightsh.graphics.Sprite;
import com.lightsh.interfaces.listener.ITouchListener;
import com.lightsh.utility.Constants;

/**
 * 所有物体的基类
 * @author Light
 *
 */
public abstract class BaseObject implements ITouchListener{
	protected Paint mPaint;
	protected Bitmap mBitmap;
	protected Rect mSrc;
	protected RectF mDst;	
	protected Matrix mMatrix;	
	protected Sprite mSprite;
	
	protected float mDrawImageOffsetX = 0;
	protected float mDrawImageOffsetY = 0;
	
	private int mDrawLevel = 0;
	private int mTouchLevel = 0;
	
	public BaseObject(){
		// FIXME 考虑冗余数据的清理，不必要的情况下不用创建过多的数据
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setColor(Color.BLUE);
		mMatrix = new Matrix();
	}
	/**
	 * 在该方法中绘制自己
	 * @param canvas 		需要绘制的canvas
	 * @param screenRect	表明当前屏幕所需要显示的范围，object需要根据自己的坐标，判断是否需要绘制自己.注意，这里使用的是px为单位
	 */
	public void draw(Canvas canvas, Rect screenRect){
		draw(canvas, screenRect, 1f, 1f);
	}
	/**
	 * 在该方法中绘制自己
	 * @param canvas 		需要绘制的canvas
	 * @param screenRect	表明当前屏幕所需要显示的范围，object需要根据自己的坐标，判断是否需要绘制自己.注意，这里使用的是px为单位
	 * @param selfScale		该值为meter/pix，使用它来将物理世界的值还原为px
	 */
	public void draw(Canvas canvas, Rect screenRect, float selfScale){
		draw(canvas, screenRect, selfScale, 1f);
	}
	
	/**
	 * 在该方法中绘制自己
	 * @param canvas 		需要绘制的canvas
	 * @param screenRect	表明当前屏幕所需要显示的范围(根据worldScale的值，可能比实际屏幕大或者小)，object需要根据自己的坐标，判断是否需要绘制自己.注意，这里使用的是px为单位
	 * @param selfScale		该值为meter/pix，使用它来将物理世界的值还原为px
	 * @param worldScale	该值为整个世界需要缩放的值，在绘制时，初始位置以及自身大小都需要进行这个比例的缩放
	 */
	public abstract void draw(Canvas canvas, Rect screenRect, float selfScale, float worldScale);
	
	/**
	 * 在该方法中更新自身数据
	 */
	public void update(){
		if(mSprite != null){
			mSprite.update();
		}
	}
	
	/**
	 * 返回物体的位置(并非重心位置)
	 */
	public abstract Vec2 getPosition();
	
	public void setColor(int color){
		mPaint.setColor(color);
	}
	
	/**
	 * 自己是否在rect范围中
	 * @param rect		目标rect
	 * @param selfScale		放大比例
	 * @return
	 */
	public boolean isInRect(Rect rect, float selfScale){
		return true;
	}
	/**
	 * 指定的点是否在自己内部
	 */
	public boolean isPointInside(Vec2 point, float worldScale, float screenX, float screenY){
		return false;
	}
	
	/**
	 * 自己是否在rect范围的上面
	 * @param rect			目标rect
	 * @param selfScale		放大比例
	 * @return
	 */
	public boolean isUpperRect(Rect rect, float selfScale){
		return false;
	}	
	
	public void bindBitmap(Bitmap bitmap){
		mBitmap = bitmap;
		if(mSrc == null){
			mSrc = new Rect();
			mDst = new RectF();
		}
	}
	
	public void bindSprite(Sprite sprite){
		mSprite = sprite;
	}
	
	/**
	 * 设置绘制图片(或者sprite)时绘制起点的偏移值，一般你不需要设置该值
	 * @param offsetX		x轴的偏移值，小于零标志向左，否者向右
	 * @param offsetY		y轴的偏移值，小于零标志向上，否者向下
	 */
	public void setDrawImageOffset(float offsetX, float offsetY){
		mDrawImageOffsetX = offsetX;
		mDrawImageOffsetY = offsetY;
	}
	
	/**
	 * 该方法会根据body的角度旋转canvas，调用该方法前后需要分别调用{@link Canvas#save()}和{@link Canvas#restore()}
	 * @param canvas			需要旋转的canvas
	 * @param objectRadian		物体的弧度值
	 * @param vec2				旋转的原点(没有进行任何转换，相对于world的坐标)
	 * @param screenRect		需要绘制的范围(需要根据该范围确定canvas实际需要旋转的原点)
	 * @param selfScale			目标body的自身缩放比例
	 * @param worldScale		整个world需要缩放的比例
	 */
	protected void rotateCanvas(Canvas canvas, float objectRadian, Vec2 vec2, Rect screenRect, float selfScale, float worldScale){
		float degrees = (objectRadian * Constants.RAD_TO_DEG) % 360;
		if (degrees != 0){
			mMatrix.reset();
			mMatrix.preRotate(degrees, (vec2.x / selfScale - screenRect.left) * worldScale, 
					(vec2.y / selfScale - screenRect.top) * worldScale);
			canvas.setMatrix(mMatrix);
		}
	}
	
	/**
	 * FIXME 我们是否需要根据所设置的body高宽对图片进行缩放？
	 * 绘制body所设置的bitmap到指定的canvas
	 * @param canvas
	 * @param left				需要绘制bitmap的左上角x坐标(原始的world坐标，没有经过转换)
	 * @param top				需要绘制bitmap的左上角y坐标(原始的world坐标，没有经过转换)
	 * @param screenRect		需要绘制的范围(需要根据该范围确定canvas实际需要旋转的原点)
	 * @param selfScale			目标body的自身缩放比例
	 * @param worldScale		整个world需要缩放的比例
	 */
	protected void drawBitmap(Canvas canvas, float left, float top, Rect screenRect, float selfScale, float worldScale){
		final int bitmapW = mBitmap.getWidth();
		final int bitmapH = mBitmap.getHeight();;
		mSrc.left = 0;
		mSrc.top = 0;
		mSrc.right = bitmapW;
		mSrc.bottom = bitmapH;
		
		mDst.left = (left / selfScale - screenRect.left + mDrawImageOffsetX) * worldScale;
		mDst.top = (top / selfScale - screenRect.top + mDrawImageOffsetY) * worldScale;
		mDst.right = mDst.left + bitmapW * worldScale;
		mDst.bottom = mDst.top + bitmapH * worldScale;
		canvas.drawBitmap(mBitmap, mSrc, mDst, mPaint);
	}
	
	public int getDrawLevel(){
		return mDrawLevel;
	}
	
	/**
	 * 设置body绘制的层次，0表示最底层，依次类推
	 * <br/>注意：设置过多的层次会导致绘制速度变慢，所以要尽量减少设置的层次。如果不是必要，请不要设置该值！
	 * @param level		注意，不能设置负数
	 */
	public void setDrawLevel(int level){
		if(level < 0){
			throw new IllegalArgumentException("level must >= 0");
		}
		mDrawLevel = level;
	}
	@Override
	public boolean onTouchEvent(MotionEvent ev, float worldScale, float screenX, float screenY) {
		return false;
	}
	@Override
	public int getTouchLevel() {
		return mTouchLevel;
	}
	/**
	 * 设置object动作响应的层次，0表示最底层，依次类推
	 * <br/>注意：设置过多的层次会导致绘制速度变慢，所以要尽量减少设置的层次。如果不是必要，请不要设置该值！
	 * @param level		注意，不能设置负数
	 */
	public void setTouchLevel(int level){
		if(level < 0){
			throw new IllegalArgumentException("level must >= 0");
		}
		mTouchLevel = level;
	}
	
}
